package com.passer.labyrinth.gui;

import com.passer.labyrinth.core.AStarAlgorithm;
import com.passer.labyrinth.core.contant.CellType;
import com.passer.labyrinth.core.util.ImgUtils;
import com.passer.labyrinth.core.util.MazeUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.Serial;
import java.util.List;

public class PasserLabyrinthPanel extends JPanel implements KeyListener {
    @Serial
    private static final long serialVersionUID = 1L;
    private final CellType[][] maze; // 迷宫
    private List<int[]> path; // 存储 A* 算法找到的路径
    private boolean isFindingPath = false; // 是否正在查找路径
    private boolean isAutoWalking = false; // 是否正在自动走迷宫

    private final int leftX;
    private final int leftY;
    private int playerX, playerY;
    private int direction;
    private int manIndex;
    private final List<Image> pics;
    private boolean showMap = false;
    private final int[] goalCoordinate;


    public static final int UP = 1, DOWN = 2, LEFT = 3, RIGHT = 4;

    public PasserLabyrinthPanel(int row, int col) {
        maze = new CellType[row][col];
        goalCoordinate = new int[]{row - 2, col - 2};
        leftX = 0;
        leftY = 0;
        this.pics = ImgUtils.getLocalImg();
        manIndex = 0;
        direction = DOWN;

        generateMaze();

        setPreferredSize(new Dimension((col + 1) * 20, (row + 1) * 20));
        addKeyListener(this);
        setFocusable(true);
    }

    // 生成迷宫
    private void generateMaze() {
        MazeUtils.initializeMaze(maze);
        maze[1][1] = CellType.ROAD;
        MazeUtils.generateMazeDFS(maze, 1, 1);
        maze[1][1] = CellType.START;
        maze[goalCoordinate[0]][goalCoordinate[1]] = CellType.GOAL;
        playerX = 1; // 初始角色位置为起点
        playerY = 1;
        maze[playerX][playerY] = CellType.PLAYER; // 设置角色位置
    }

    @Override
    public void paint(Graphics g) {
        if (showMap) {
            printAllWall(g);
            return;
        }
        paintWall(g);
    }

    private void paintWall(Graphics g) {
        g.clearRect(0, 0, getWidth(), getHeight());
        g.fillRect(0, 0, getWidth(), getHeight());
        for (int i = playerX - 1; i <= playerX + 1; i++) {
            for (int j = playerY - 1; j <= playerY + 1; j++) {
                paintWall(g, i, j);
            }
        }
        g.drawOval(leftX + 20 * (playerY - 1), leftX + 20 * (playerX - 1), 60, 60);// 画圆
        for (int i = 20 * (playerY - 1); i <= 20 * (playerY + 2); i++) {
            for (int j = 20 * (playerX - 1); j <= 20 * (playerX + 2); j++) {
                if ((i - (20 * playerY + 10)) * (i - (20 * playerY + 10)) + (j - (20 * playerX + 10)) * (j - (20 * playerX + 10)) > 30 * 30) {
                    g.fillRect(i, j, 1, 1);
                }
            }
        }
    }

    private void printAllWall(Graphics g) {
        g.clearRect(0, 0, getWidth(), getHeight());
        for (int i = 0; i < maze.length; i++) {
            for (int j = 0; j < maze[0].length; j++) {
                paintWall(g, i, j);
            }
        }
    }

    private void paintWall(Graphics g, int i, int j) {
        switch (maze[i][j]) {
            case ROAD:
                g.drawImage(pics.get(1), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                break;
            case PLAYER:
                switch (direction) {
                    case UP:
                        g.drawImage(pics.get(1), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        g.drawImage(pics.get(3 + manIndex), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        break;
                    case DOWN:
                        g.drawImage(pics.get(1), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        g.drawImage(pics.get(7 + manIndex), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        break;
                    case LEFT:
                        g.drawImage(pics.get(1), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        g.drawImage(pics.get(11 + manIndex), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        break;
                    case RIGHT:
                        g.drawImage(pics.get(1), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        g.drawImage(pics.get(15 + manIndex), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                        break;
                }
                break;
            case WALL:
                g.drawImage(pics.get(0), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
                break;
            default:
                g.drawImage(pics.get(2), leftX + 20 * j, leftY + 20 * i, 20, 20, this);
        }
    }

    @Override
    public void keyPressed(KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_UP:
                if (maze[playerX - 1][playerY] != CellType.WALL) {
                    if (direction == UP) {
                        manIndex = manIndex + 1 == 4 ? 0 : ++manIndex;
                    } else {
                        manIndex = 0;
                        direction = UP;
                    }
                    maze[playerX][playerY] = CellType.ROAD;
                    playerX -= 1;
                    maze[playerX][playerY] = CellType.PLAYER;
                }
                break;
            case KeyEvent.VK_DOWN:
                if (maze[playerX + 1][playerY] != CellType.WALL) {
                    if (direction == DOWN) {
                        manIndex = manIndex + 1 == 4 ? 0 : ++manIndex;
                    } else {
                        manIndex = 0;
                        direction = DOWN;
                    }
                    maze[playerX][playerY] = CellType.ROAD;
                    playerX += 1;
                    maze[playerX][playerY] = CellType.PLAYER;
                }
                break;
            case KeyEvent.VK_LEFT:
                if (maze[playerX][playerY - 1] != CellType.WALL) {
                    if (direction == LEFT) {
                        manIndex = manIndex + 1 == 4 ? 0 : ++manIndex;
                    } else {
                        manIndex = 0;
                        direction = LEFT;
                    }
                    maze[playerX][playerY] = CellType.ROAD;
                    playerY -= 1;
                    maze[playerX][playerY] = CellType.PLAYER;
                }
                break;
            case KeyEvent.VK_RIGHT:
                if (maze[playerX][playerY + 1] != CellType.WALL) {
                    if (direction == RIGHT) {
                        manIndex = manIndex + 1 == 4 ? 0 : ++manIndex;
                    } else {
                        manIndex = 0;
                        direction = RIGHT;
                    }
                    maze[playerX][playerY] = CellType.ROAD;
                    playerY += 1;
                    maze[playerX][playerY] = CellType.PLAYER;
                }
                break;
            case KeyEvent.VK_H:
                // 移除地图阴影
                this.showMap = !this.showMap;
                break;
            case KeyEvent.VK_A:
                // 自动寻路
                autoWalk();
                break;
        }
        repaint();
        if (maze != null && goalCoordinate[0] == playerX && goalCoordinate[1] == playerY) {
            JOptionPane.showMessageDialog(this, "恭喜过关");
            reGenerate();
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    /**
     * 重新生成迷宫
     */
    private void reGenerate() {
        generateMaze();
        path = null; // 清除路径
        isAutoWalking = false; // 停止自动走迷宫
        this.repaint(); // 刷新迷宫面板
    }

    /**
     * 查找通关路径
     */
    private void findPath() {
        if (isFindingPath || isAutoWalking) return; // 避免重复操作
        isFindingPath = true;

        /*
         找到起点和终点
         迷宫地图初始化后角色覆盖了起点
         */
        int[] start = MazeUtils.findPosition(maze, CellType.PLAYER);
        int[] goal = MazeUtils.findPosition(maze, CellType.GOAL);

        // 使用 A* 算法查找路径
        path = AStarAlgorithm.findPath(maze, start, goal);

        if (path.isEmpty()) {
            JOptionPane.showMessageDialog(this, "未找到通关路径", "Error", JOptionPane.ERROR_MESSAGE);
        }
    }

    /**
     * 根据通关路径自动走迷宫
     */
    private void autoWalk() {
        if (isFindingPath || isAutoWalking) return; // 避免重复操作
        // 如果路径为空，先查找路径
        if (path == null || path.isEmpty()) {
            findPath(); // 触发路径查找
        }
        isAutoWalking = true;

        // 开始自动走迷宫
        Timer timer = new Timer(200, event -> {
            if (!path.isEmpty()) {
                int[] nextStep = path.get(0); // 获取下一步
                maze[playerX][playerY] = CellType.ROAD; // 将当前角色位置恢复为路径
                // 判断角色行走方向
                String changeType = "" + (playerX - nextStep[0]) + (playerY - nextStep[1]);
                switch (changeType) {
                    case "01":
                        direction = LEFT;
                        break;
                    case "0-1":
                        direction = RIGHT;
                        break;
                    case "10":
                        direction = UP;
                        break;
                    case "-10":
                        direction = DOWN;
                }
                playerX = nextStep[0]; // 更新角色位置
                playerY = nextStep[1];
                maze[playerX][playerY] = CellType.PLAYER; // 设置新角色位置
                path.remove(0); // 移除已走过的步骤
                this.repaint();
            } else {
                ((Timer) event.getSource()).stop(); // 停止计时器
                isAutoWalking = false;
                JOptionPane.showMessageDialog(this, "恭喜过关");
                reGenerate();
            }
        });
        timer.start();
    }


}
